#include "src\precompiled.h"
#include "src\mesh.h"
#include "src\skeleton.h"
#include "src\animation.h"

static inline vector3 extract_translation(const matrix4 &tm) { return tm.extract_translation(); }

static inline vector3 extract_rotation(const matrix4 &tm)
{
	float yaw, pitch, roll;
	matrix3 m = tm.extract_matrix3();
	m.to_euler_angles(yaw, pitch, roll);
	return vector3(yaw, pitch, roll);
}

static inline vector3 extract_scale(const matrix4 &tm)
{
	vector3 pos, sc;
	quaternion rot;
	tm.decomposition(pos, sc, rot);
	return sc;
}

node_animation_track::node_animation_track()
	:	m_length(0)
{ }

node_animation_track::~node_animation_track() { }

size_t node_animation_track::add_key(size_t time, const matrix4 &tm)
{
	m_keys.push_back(keyframe(extract_translation(tm),
		extract_rotation(tm), extract_scale(tm), time));
	m_length = max(m_length, time);
	return get_num_keys() - 1;
}

void node_animation_track::set_key_values(int anim, int axis, const std::vector<ILinFloatKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ILinFloatKey &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const ILinFloatKey &next = val[i];
		switch(anim)
		{
		case TRANSLATION:
			m_keys[i].translation[axis] = next.val - first.val;
			break;
		case ROTATION:
			m_keys[i].rotation[axis] = next.val - first.val;
			break;
		case SCALE:
			m_keys[i].scale[axis] = (next.val / first.val);
			break;
		}
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(int anim, int axis, const std::vector<IBezFloatKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const IBezFloatKey &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const IBezFloatKey &next = val[i];
		switch(anim)
		{
		case TRANSLATION:
			m_keys[i].translation[axis] = next.val - first.val;
			break;
		case ROTATION:
			m_keys[i].rotation[axis] = next.val - first.val;
			break;
		case SCALE:
			m_keys[i].scale[axis] = (next.val / first.val);
			break;
		}
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(int anim, int axis, const std::vector<ITCBFloatKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ITCBFloatKey &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const ITCBFloatKey &next = val[i];
		switch(anim)
		{
		case TRANSLATION:
			m_keys[i].translation[axis] = next.val - first.val;
			break;
		case ROTATION:
			m_keys[i].rotation[axis] = next.val - first.val;
			break;
		case SCALE:
			m_keys[i].scale[axis] = (next.val / first.val);
			break;
		}
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<ILinPoint3Key> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ILinPoint3Key &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const ILinPoint3Key &next = val[i];
		m_keys[i].translation.x = next.val.x - first.val.x;
		m_keys[i].translation.y = next.val.y - first.val.y;
		m_keys[i].translation.z = next.val.z - first.val.z;
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<IBezPoint3Key> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const IBezPoint3Key &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const IBezPoint3Key &next = val[i];
		m_keys[i].translation.x = next.val.x - first.val.x;
		m_keys[i].translation.y = next.val.y - first.val.y;
		m_keys[i].translation.z = next.val.z - first.val.z;
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<ITCBPoint3Key> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ITCBPoint3Key &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const ITCBPoint3Key &next = val[i];
		m_keys[i].translation.x = next.val.x - first.val.x;
		m_keys[i].translation.y = next.val.y - first.val.y;
		m_keys[i].translation.z = next.val.z - first.val.z;
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<ILinRotKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ILinRotKey &first = val[0];
	for(size_t i = 1; i < count; i++) {
		const ILinRotKey &next = val[i];
		m_keys[i].rotation.x = next.val.x - first.val.x;
		m_keys[i].rotation.y = next.val.y - first.val.y;
		m_keys[i].rotation.z = next.val.z - first.val.z;
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<IBezQuatKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	float a1, b1, c1;
	const IBezQuatKey &first = val[0];
	Quat q1(first.val.x, first.val.y, first.val.z, first.val.w);
	q1.GetEuler(&a1, &b1, &c1);
	for(size_t i = 1; i < count; i++) {
		float a2, b2, c2;
		const IBezQuatKey &next = val[i];
		Quat q1(next.val.x, next.val.y, next.val.z, next.val.w);
		q1.GetEuler(&a2, &b2, &c2);
		m_keys[i].rotation.x = a2 - a1;
		m_keys[i].rotation.y = b2 - b1;
		m_keys[i].rotation.z = c2 - c1;
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<ITCBRotKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	float a1, b1, c1;
	const ITCBRotKey &first = val[0];
	Quat q1(first.val.axis, first.val.angle);
	q1.GetEuler(&a1, &b1, &c1);
	for(size_t i = 1; i < count; i++) {
		float a2, b2, c2;
		const ITCBRotKey &next = val[i];
		Quat q2(first.val.axis, first.val.angle);
		q2.GetEuler(&a2, &b2, &c2);
		m_keys[i].rotation.x = a2 - a1;
		m_keys[i].rotation.y = b2 - b1;
		m_keys[i].rotation.z = c2 - c1;
		m_keys[i].time = next.time;
		m_length = max(m_length, next.time);
	}
}

void node_animation_track::set_key_values(const std::vector<ILinScaleKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ILinScaleKey &first = val[0];
	for(size_t i = 1; i < val.size(); i++) {
		m_keys[i].scale.x = val[i].val.s.x / first.val.s.x;
		m_keys[i].scale.y = val[i].val.s.y / first.val.s.y;
		m_keys[i].scale.z = val[i].val.s.z / first.val.s.z;
		m_keys[i].time = val[i].time;
		m_length = max(m_length, val[i].time);
	}
}

void node_animation_track::set_key_values(const std::vector<IBezScaleKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const IBezScaleKey &first = val[0];
	for(size_t i = 0; i < val.size(); i++) {
		m_keys[i].scale.x = val[i].val.s.x / first.val.s.x;
		m_keys[i].scale.y = val[i].val.s.y / first.val.s.y;
		m_keys[i].scale.z = val[i].val.s.z / first.val.s.z;
		m_keys[i].time = val[i].time;
		m_length = max(m_length, val[i].time);
	}
}

void node_animation_track::set_key_values(const std::vector<ITCBScaleKey> &val)
{
	size_t count = val.size();
	if(m_keys.size() <= count)
		m_keys.resize(count);

	const ITCBScaleKey &first = val[0];
	for(size_t i = 0; i < val.size(); i++) {
		m_keys[i].scale.x = val[i].val.s.x / first.val.s.x;
		m_keys[i].scale.y = val[i].val.s.y / first.val.s.y;
		m_keys[i].scale.z = val[i].val.s.z / first.val.s.z;
		m_keys[i].time = val[i].time;
		m_length = max(m_length, val[i].time);
	}
}

void node_animation_track::clear() { m_keys.clear(); m_length = 0; }

size_t node_animation_track::get_num_keys() const { return m_keys.size(); }

vector3 node_animation_track::get_key_translation(size_t key) const { return m_keys[key].translation; }

vector3 node_animation_track::get_key_rotation(size_t key) const { return m_keys[key].rotation; }

vector3 node_animation_track::get_key_scale(size_t key) const { return m_keys[key].scale; }

size_t node_animation_track::get_key_time(size_t key) const { return m_keys[key].time; }

node_animation_track::keyframe::keyframe()
{
	scale = vector3(1.0f, 1.0f, 1.0f);
	time = 0;
}

node_animation_track::keyframe::keyframe(const vector3 &_translation, const vector3 &_rotation, const vector3 &_scale, size_t _time)
	:	translation(_translation),
		rotation(_rotation),
		scale(_scale),
		time(_time)
{ }


void node_animation_track::apply_initial_transforms(const matrix4 &tm)
{
	vector3 translation = extract_translation(tm);
	vector3 rotation = extract_rotation(tm);
	vector3 scale = extract_scale(tm);

	for(size_t i = 0; i < m_keys.size(); i++) {
		keyframe &kf = const_cast<keyframe&>(m_keys[i]);
		kf.translation += translation;
		kf.rotation += rotation;
		kf.scale *= scale;
	}
}

size_t node_animation_track::get_length() const { return m_length; }

//-----------------------------------------------------------------------------

vertex_animation_track::vertex_animation_track() : m_length(0) { }

vertex_animation_track::~vertex_animation_track() { }

void vertex_animation_track::clear()
{
	m_keys.clear();
	m_length = 0;
}
void vertex_animation_track::add_key() { m_keys.push_back(keyframe()); }

void vertex_animation_track::add_key(size_t time, mesh_ptr pose) { m_keys.push_back(keyframe(time, pose)); }

void vertex_animation_track::set_time(size_t key, size_t time)
{
	if(key >= m_keys.size())
		m_keys.resize(key + 1);
	m_keys[key].time = time;
}

void vertex_animation_track::set_pose(size_t key, mesh_ptr pose)
{
	if(key >= m_keys.size())
		m_keys.resize(key + 1);
	m_keys[key].pose = pose;
}

size_t vertex_animation_track::get_key_count() const { return m_keys.size(); }

size_t vertex_animation_track::get_length() const { return 0; }

size_t vertex_animation_track::get_time(size_t key) const { return m_keys[key].time; }

mesh_ptr vertex_animation_track::get_pose(size_t key) const { return m_keys[key].pose; }

vertex_animation_track::keyframe::keyframe() : time(0) { }

vertex_animation_track::keyframe::keyframe(size_t _time, mesh_ptr _pose) : time(_time), pose(_pose) { }

//-----------------------------------------------------------------------------

animation::animation() { }

animation::~animation() { }

void animation::clear()
{
	m_name.clear();
	m_node_tracks.clear();
}

void animation::set_name(const std::string &name) { m_name = name; }

void animation::add_node_animation_track(const node_animation_track &track) { m_node_tracks.push_back(track); }

void animation::add_vertex_animation_track(const vertex_animation_track &track) { m_vertex_tracks.push_back(track); }

bool animation::has_node_animation_track() const { return !m_node_tracks.empty(); }

bool animation::has_vertex_animation_track() const { return !m_vertex_tracks.empty(); }

std::string animation::get_name() const { return m_name; }

size_t animation::get_length() const
{
	size_t length = 0;
	for(size_t i = 0; i < m_node_tracks.size(); i++) {
		size_t l = m_node_tracks[i].get_length();
		length = (length >= l) ? length : l;
	}
	return length;
}

size_t animation::get_num_node_animations() const { return m_node_tracks.size(); }

size_t animation::get_num_vertex_animations() const { return m_vertex_tracks.size(); }

node_animation_track animation::get_node_animation_track(size_t id) const { return m_node_tracks[id]; }

vertex_animation_track animation::get_vertex_animation_track(size_t id) const { return m_vertex_tracks[id]; }